#include common_scripts\utility;
#include maps\mp\_utility;
#include maps\mp\gametypes\_hud_util;
#include z_mod\utility;
#using_animtree("multiplayer");


init()
{
    precacheAnimations();
    
    level.zAI_NPCs = [];
    
    level thread AI_Spawner();
    level thread masterBot_Spawner();
}

AI_AttackPlayers()
{
    self endon("death");
    self endon("zdeath");
    
    for(;;) {
        if(self.isAlarmed)
            wait(0.2);
        else
            wait(1.0 - self.extraVision/200.0);
        
        if(self.isBeingFlashed) // бот контужен флешкой
            continue;
        
        targets = []; // потенциальные цели для атаки
        
        // начинаем первичную проверку
        for(i = 0; i < level.zClass_List.size; i++) { // проверяем только игроков, выбравших класс
            if(!isDefined(level.zClass_List[i].busy))
                continue;
            
            enemy = level.zClass_List[i].busy;
            
            if(enemy.zMain_AtBase) // игрок на базе?
                continue;
            
            trace_to_enemy = bulletTrace(self getTagOrigin("tag_eye"), enemy getTagOrigin("j_spine4"), true, self.head);
            
            if(!isDefined(trace_to_enemy["entity"]) || trace_to_enemy["entity"] != enemy) // игрок не виден?
                continue;
            
            // первичная проверка закончена
            targets[targets.size] = enemy;
        }
        
        // начинаем вторичную проверку
        if(targets.size) {
            targets2 = []; // игроки, которых бот заметил и готов атаковать
            
            if(self.isAlarmed) { // встревожен - более тщательная проверка
                for(i = 0; i < targets.size; i++) {
                    if(distance2d(targets[i].origin, self.origin) > 5000 + self.extraVision*75)
                        continue;
                    
                    if(distance2d(targets[i].origin, level.zMain_LastTroubleOrigin) < 1300) { // находится в эпицентре внимания
                        if(!targets[i] getVelocity() && targets[i] getStance() == "prone") { // лежит и не двигается
                            continue;
                        } else if(randomInt(100) < (80 - self.extraVision)) {
                            continue;
                        }
                    }
                    
                    targets2[targets2.size] = targets[i];
                }
            } else {
                //
            }
            
            if(targets2.size) { // выбираем приоритетную цель
                
            }
        }
    }
}

AI_CoolDown(time)
{
    self notify("zcooldown");
    self endon("zcooldown");
    self endon("death");
    self endon("zdeath");
    
    if(self.coolDown < time)
        self.coolDown = time;
    
    self.isAlarmed = true;
    
    for(self.coolDown > 0) {
        wait(1.0);
        self.coolDown--;
    }
    
    self.isAlarmed = false;
}

AI_Damage()
{
    last_attacker = undefined;
    
    while(self.box.hp > 0) {
        self.box waittill("damage", damage, attacker, direction_vec, point, type, modelName, tagName, partName, iDFlags, sWeapon);
        
        last_attacker = attacker;
        
        // +50% урона от тяжелых снайперских винтовок
        if(sWeapon == "cheytac_mp" || sWeapon == "barrett_mp") {
            damage *= 1.5;
        }
        
        // flash_grenade_mp, concussion_grenade_mp
        if(sWeapon == "flash_grenade_mp" || type == "MOD_GRENADE_SPLASH") {
            self thread AI_Shellshock();
        }
        
        ///level.zMasterBot sayAll("point: " + point); // координаты
        ///level.zMasterBot sayAll("type: " + type); // тип (MOD_MELEE, MOD_IMPACT, etc)
        ///level.zMasterBot sayAll("partName: " + partName); // tag
        ///level.zMasterBot sayAll("sWeapon: " + sWeapon); // оружие
        
        self.box.hp -= damage;
        
        self thread AI_CoolDown(120);
    }
    
    // временно +1 за убийство в HUDе
    last_attacker thread maps\mp\gametypes\_rank::giveRankXP("kill", 1);
    maps\mp\gametypes\_gamescore::givePlayerScore("kill", last_attacker);
    
    // +1 убийство в стату
    last_attacker incPersStat("kills", 1);
    last_attacker.kills = last_attacker getPersStat("kills");
    last_attacker updatePersRatio("kdRatio", "kills", "deaths");
    last_attacker maps\mp\gametypes\_persistence::statSetChild("round", "kills", last_attacker.kills);
    last_attacker incPlayerStat("kills", 1);
    
    self AI_Death();
}

AI_Death()
{
    self notify("zdeath");
    
    if(self.isCrouching)
        self thread AI_DeathProcess(self.anims["death_crouch"], 30);
    else
        self thread AI_DeathProcess(self.anims["death_stand"], 30);
    
    wait(0.05);
    
    self.vec delete();
    self.weapon delete();
    self.head delete();
    self delete();
}

AI_DeathProcess(death_anim, duration)
{
    self.box delete();
    
    death_body = spawn("script_model", self.origin);
    death_body setModel(level.zMap_DefendersUnits[self.type].body);
    
    death_body.head = spawn("script_model", death_body getTagOrigin("j_spine4"));
    death_body.head.angles = (270, 0, 270);
    death_body.head setModel(level.zMap_DefendersUnits[self.type].head);
    death_body.head linkTo(death_body, "j_spine4");
    
    death_body.weapon = spawn("script_model", death_body getTagOrigin("tag_weapon_right"));
    death_body.weapon setModel(getWeaponModel(self.weapon.name, self.weapon.camo));
    death_body.weapon z_WeaponModelManageTags(self.weapon.name);
    death_body.weapon linkTo(death_body, "tag_weapon_right");
    
    self.weapon hide();
    self.head hide();
    self hide();
    
    death_body scriptModelPlayAnim(death_anim);
    death_body.head scriptModelPlayAnim(death_anim);
    
    wait(duration);
    
    death_body.weapon delete();
    death_body.head delete();
    death_body delete();
}

AI_GetFreeSlot()
{
    slot = 0;
    
    while(isDefined(level.zAI_NPCs[slot])) {
        slot++;
    }
    
    return slot;
}

AI_HearingShots()
{
    self endon("death");
    self endon("zdeath");
    
    for(;;) {
        while(level.zMain_LastTroubleTime + 250 < getTime())
            wait(0.05);
        
        self thread AI_CoolDown(60);
    }
}

AI_Shellshock()
{
    self notify("zshellshock");
    self endon("zshellshock");
    self endon("death");
    self endon("zdeath");
    
    self.isBeingFlashed = true;
    
    if(self.isMoving) {
        // прекращаем движение
        self moveTo(self.origin, 0.05);
        self.isMoving = false;
    }
    
    if(self.isCrouching) {
        self scriptModelPlayAnim(self.anims["shellshock_crouch"]);
        self.head scriptModelPlayAnim(self.anims["shellshock_crouch"]);
    } else {
        self scriptModelPlayAnim(self.anims["shellshock_stand"]);
        self.head scriptModelPlayAnim(self.anims["shellshock_stand"]);
    }
    
    wait(3.5);
    
    self.isBeingFlashed = false;
    
    if(self.isCrouching) {
        self scriptModelPlayAnim(self.anims["ads_crouch"]);
        self.head scriptModelPlayAnim(self.anims["ads_crouch"]);
    } else {
        self scriptModelPlayAnim(self.anims["ads_stand"]);
        self.head scriptModelPlayAnim(self.anims["ads_stand"]);
    }
}

AI_Spawner()
{
    level endon("game_ended");
    
    level waittill("zmasterbot_spawned");
    
    for(;;) {
        wait(60);
        
        if(cointoss()) { // каждые 60 сек пытаемся заспавнить (рандомная позиция + проверка на незанятость) снайпера с шансом 50/50
            level.zAI_NPCs[AI_GetFreeSlot()] = AI_SpawnSniperUnit();
        }
    }
}

AI_SpawnSniperUnit(spawn_num) // снайпер - урезанная версия обычного юнита
{
    if(!isDefined(spawn_num) || spawn_num >= level.zMap_DefendersSniperSpawns.size)
        spawn_num = randomInt(level.zMap_DefendersSniperSpawns.size);
    
    spawn_origin = level.zMap_DefendersSniperSpawns[spawn_num].origin;
    spawn_angles = level.zMap_DefendersSniperSpawns[spawn_num].angles;
    
    if(isDefined(level.zMap_DefendersSniperSpawns[spawn_num].busy))
        return undefined;
    
    unit = spawn("script_model", spawn_origin);
    unit.angles = spawn_angles;
    unit setModel(level.zMap_DefendersUnits["_sniper"].body);
    
    unit.head = spawn("script_model", unit getTagOrigin("j_spine4"));
    unit.head.angles = (270, 0, 270);
    unit.head setModel(level.zMap_DefendersUnits["_sniper"].head);
    unit.head linkTo(unit, "j_spine4");
    
    unit.weapon = spawn("script_model", unit getTagOrigin("tag_weapon_right"));
    unit.weapon.name = level.zMap_DefendersUnits["_sniper"].weapon;
    unit.weapon.camo = level.zMap_DefendersUnits["_sniper"].camo;
    unit.weapon.delay = level.zMap_DefendersUnits["_sniper"].delay;
    unit.weapon setModel(getWeaponModel(unit.weapon.name, unit.weapon.camo));
    unit.weapon z_WeaponModelManageTags(unit.weapon.name);
    unit.weapon linkTo(unit, "tag_weapon_right");
    
    unit.vec = spawn("script_origin", unit getTagOrigin("tag_weapon_right"));
    unit.vec linkTo(unit.weapon, "tag_flash", (50, 0, 0), (0, 0, 0));
    
    unit.box = spawn("script_model", unit getTagOrigin("j_spinelower") + (-5, 0, -5));
    unit.box setModel("com_plasticcase_beige_big");
    unit.box cloneBrushmodelToScriptmodel(level.airDropCrateCollision);
    unit.box.angles = (90, 0, 0);
    //unit.box hide();
    unit.box setCanDamage(true);
    unit.box.health = 9999;
    unit.box.maxhealth = 9999;
    unit.box.hp = 100;
    unit.box linkTo(unit, "j_spinelower");
    
    unit.extraVision = 50.0; // [1..100] - дает "бафф" к обнаружению игроков
    unit.skill = 0.8; // не работает
    unit.type = "_sniper";
    
    unit.anims = [];
    unit.anims["ads_crouch"] = "pb_crouch_ads"; // атакует, встревожен, ищет цель
    unit.anims["death_crouch"] = "pb_crouch_death_clutchchest"; // умирает
    unit.anims["idle_crouch"] = "pb_crouch_alert"; // ничего не делает
    unit.anims["shellshock_crouch"] = "pb_crouch_shellshock"; // ослеплен флешкой
    
    unit.isAlarming = false;
    unit.isBeingFlashed = false;
    unit.isCrouching = true;
    unit.isMoving = false;
    unit.isShooting = false;
    
    unit.coolDown = 0;
    unit.lastTarget = undefined;
    
    unit scriptModelPlayAnim(unit.anims["idle_crouch"]);
    unit.head scriptModelPlayAnim(unit.anims["idle_crouch"]);
    
    //unit rotateTo(spawn_angles, 0.5);
    
    unit thread AI_AttackPlayers();
    unit thread AI_Damage();
    
    level.zMap_DefendersSniperSpawns[spawn_num].busy = unit;
    
    return unit;
}

masterBot_Logic()
{
    self endon("disconnect");
    
    while(!isDefined(self.pers["team"]))
        wait(0.1);
    
    self notify("menuresponse", game["menu_team"], level.zCfg_Defenders);
    
    wait(0.5);
    
    for(;;) {
        self notify("menuresponse", "changeclass", "class4");
        
        self waittill("spawned_player");
        self setOrigin(level.zMap_MasterBotSpawnOrigin);
        self setPlayerAngles(level.zMap_MasterBotSpawnAngles);
        
        level notify("zmasterbot_spawned", self);
        
        wait(0.1);
    }
}

masterBot_Spawner()
{
    z_WaittillGameCreated();
    wait(3.5); // не спавним мастер-бота слишком рано
    
    for(;;) {
        level.zMasterBot = addTestClient();
        
        if(isDefined(level.zMasterBot)) {
            level.zMasterBot.pers["master_bot"] = true;
            level.zMasterBot thread masterBot_Logic();
            
            level.zMasterBot waittill("disconnect");
        }
        
        wait(0.25);
    }
}

precacheAnimations()
{
    //precacheMpAnim("pb_stand_death_shoulder_stumble"); // смерть 1
    //precacheMpAnim("pb_stumble_walk_forward"); // walking
    //precacheMpAnim("pb_walk_forward_mg"); // ходьба джаггернаута
    //precacheMpAnim("pb_run_fast"); // очень быстрый забавный бег
    //precacheMpAnim("pb_sprint"); // бежит так быстро, что аж бомбит
    //precacheMpAnim("pb_sprint_gundown"); // бежит быстро, норм так
    //precacheMpAnim("pb_sprint_hold"); // бежит быстро с С4
    //precacheMpAnim("pt_reload_stand_rifle"); // перезарядка быстрая
    //precacheMpAnim("pt_reload_stand_mg"); // перезарядка долгая (пулемет)
    //precacheMpAnim("pb_stand_shoot_walk_forward_unarmed"); // анимация ходьбы в развалочку
    //precacheMpAnim("pb_stand_shoot_walk_forward");
    //precacheMpAnim("pb_stand_shellshock"); // контузия
    
    // бездействие
    precacheMpAnim("pb_stand_alert"); // стоя
    precacheMpAnim("pb_crouch_alert"); // сидя
    
    // ходьба
    precacheMpAnim("pb_walk_forward_mg"); // стоя
    
    // бездействие, в руках С4
    //precacheMpAnim("pb_hold_idle"); // стоя
    //precacheMpAnim("pb_crouch_hold_idle"); // сидя
    
    // прицеливание/стрельба на месте
    precacheMpAnim("pb_stand_ads"); // стоя
    precacheMpAnim("pb_crouch_ads"); // сидя
    
    // смерть
    precacheMpAnim("pb_stand_death_shoulder_stumble"); // стоя
    precacheMpAnim("pb_crouch_death_clutchchest"); // сидя
    
    // контузия
    precacheMpAnim("pb_stand_shellshock"); // стоя
    precacheMpAnim("pb_crouch_shellshock"); // сидя
    
    // использование ноутбука
    //precacheMpAnim("pb_stand_remotecontroller"); // стоя
    //precacheMpAnim("pb_crouch_remotecontroller"); // сидя
}






































































































































































































































































/*
init()
{
    precacheAnimations();
    
    level thread masterBot_Spawner();
    level thread AI_Manager();
}

AI_AttackPlayers()
{
    self endon("death");
    self endon("zdeath");
    
    for(;;) {
        targets = [];
        
        for(i = 0; i < level.zClass_List.size; i++) { // аттакуем только тех игроков, которые выбрали класс
            enemy = undefined;
            
            if(!isDefined(level.zClass_List[i].busy))
                continue;
            else
                enemy = level.zClass_List[i].busy;
            
            if(enemy.zMain_AtBase) // игрок на базе
                continue;
            
            if(self.isBeingFlashed) // бот контужен флешкой
                continue;
            
            trace_to_enemy = bulletTrace(self.origin + (0, 0, 75), enemy getTagOrigin("j_spine4"), true, self.head);
            
            if(!isDefined(trace_to_enemy["entity"]) || trace_to_enemy["entity"] != enemy) // игрок не виден?
                continue;
            
            // игрок доступен для атаки -> добавляем его в массив целей для последующей эмуляции видимости ботом
            targets[targets.size] = enemy;
            
            // по звукам выстрелов
            if((enemy.zMain_LastAttackTime) < (getTime() + 20000)) {             
                //
            }
            
            chance = randomInt(100);
            
            if(enemy.isSniper) {
                if(chance+self.extraVision < level.zCfg_GhillieCamouflage[enemy getStance()])
                    continue;
            } else {
                if(chance+self.extraVision < level.zCfg_CommonCamouflage[enemy getStance()])
                    continue;
            }
            
            // атакуем игрока
            self AI_AttackSpecificPlayer(enemy); // выступает в роли waittill()
            self.isShooting = false;
        }
        
        if(targets.size == 1) {
            if(targets[0].isSniper) {
                //
            } else {
                //
            }
            
            
            
            if((enemy.zMain_LastAttackTime + 20000) > getTime()) {      // 20.000 + 20.000        40.000
            
            }
        } else if(targets.size > 1) {
            //
        }
        
        wait(0.5);
    }
}

AI_AttackSpecificPlayer(enemy)
{
    self endon("death");
    self endon("zdeath");
    self endon("zshellshock");
    
    if(!self.isShooting) {
        self moveTo(self.origin, 0.1); // stop moving
        self.isShooting = true;
    }
    
    if(self.isCrouching)
        self scriptModelPlayAnim(self.anims["ads_crouch"]);
    else
        self scriptModelPlayAnim(self.anims["ads_stand"]);
    
    while(bulletTracePassed(self.origin + (0, 0, 75), enemy getTagOrigin("tag_eye"), false, self.head) && enemy.health > 0 && !self.isBeingFlashed) {
        angles_to_enemy = vectorToAngles(enemy.origin - self.origin);
        dist_to_enemy = distance(enemy.origin, self.origin);
        
        if(self.extraVision) {
            if(dist_to_enemy > 1000)
                bulletOffset = (randomIntRange(-15, 15), randomIntRange(-15, 15), randomIntRange(-100, 100)); // снайпер мажет по Z
            else
                bulletOffset = (0, 0, 0);
        } else {
            if(dist_to_enemy > 150)
                bulletOffset = (randomIntRange(-40, 40), randomIntRange(-40, 40), randomIntRange(-40, 40));
            else
                bulletOffset = (0, 0, 0);
        }
        
        self rotateTo((0, angles_to_enemy[1], 0), 0.1);
        magicBullet(self.weapon.name, self.weapon getTagOrigin("tag_flash"), enemy getTagOrigin("j_spine4") + bulletOffset, level.zMasterBot);
        
        wait(self.weapon.delay);
    }
}

AI_CreateAlarm()
{
    alarm = spawnStruct();
    alarm.enabled = false;
    alarm.trouble_origin = (0, 0, 0);
    alarm.counter = 0;
    
    return alarm;
}

AI_Damage()
{
    last_attacker = undefined;
    score_amount = 1;
    
    while(self.box.hp > 0) {
        self.box waittill("damage", damage, attacker, direction_vec, point, type, modelName, tagName, partName, iDFlags, sWeapon);
        
        last_attacker = attacker;
        
        // extra damage from heavy sniper rifles
        if(sWeapon == "cheytac_mp" || sWeapon == "barrett_mp") {
            damage *= 1.5;
        }
        
        // flash_grenade_mp + concussion_grenade_mp
        if(sWeapon == "flash_grenade_mp" || type == "MOD_GRENADE_SPLASH") {
            self thread AI_Shellshock();
        }
        
        self.box.hp -= damage;
    }
    
    //if(self.type == "sniper")
    //    score_amount *= 3;
    
    // временно +1 за убийство в HUDе
    last_attacker thread maps\mp\gametypes\_rank::giveRankXP("kill", 1);
    maps\mp\gametypes\_gamescore::givePlayerScore("kill", last_attacker);
    
    // +1 убийство в стату
    last_attacker incPersStat("kills", 1);
    last_attacker.kills = last_attacker getPersStat("kills");
    last_attacker updatePersRatio("kdRatio", "kills", "deaths");
    last_attacker maps\mp\gametypes\_persistence::statSetChild("round", "kills", last_attacker.kills);
    last_attacker incPlayerStat("kills", 1);
    
    self AI_Death();
}

AI_Death()
{
    self notify("zdeath");
    
    if(self.isCrouching)
        self thread AI_DeathProcess(self.anims["death_crouch"], 30);
    else
        self thread AI_DeathProcess(self.anims["death_stand"], 30);
    
    wait(0.05);
    
    self.vec delete();
    self.weapon delete();
    self.head delete();
    self delete();
}

AI_DeathProcess(death_anim, duration)
{
    self.box delete();
    
    death_body = spawn("script_model", self.origin);
    death_body setModel(level.zMap_DefendersUnits[self.type].body);
    
    death_body.head = spawn("script_model", death_body getTagOrigin("j_spine4"));
    death_body.head.angles = (270, 0, 270);
    death_body.head setModel(level.zMap_DefendersUnits[self.type].head);
    death_body.head linkTo(death_body, "j_spine4");
    
    death_body.weapon = spawn("script_model", death_body getTagOrigin("tag_weapon_right"));
    death_body.weapon setModel(getWeaponModel(self.weapon.name, self.weapon.camo));
    death_body.weapon z_WeaponModelManageTags(self.weapon.name);
    death_body.weapon linkTo(death_body, "tag_weapon_right");
    
    self.weapon hide();
    self.head hide();
    self hide();
    
    death_body scriptModelPlayAnim(death_anim);
    death_body.head scriptModelPlayAnim(death_anim);
    
    wait(duration);
    
    death_body.weapon delete();
    death_body.head delete();
    death_body delete();
}

AI_GetFreeSlot()
{
    if(!isDefined(level.zAI_NPCs)) {
        level.zAI_NPCs = [];
        return 0;
    }
    
    slot = 0;
    
    while(isDefined(level.zAI_NPCs[slot])) {
        slot++;
    }
    
    return slot;
}

AI_Manager()
{
    level.zAI_NPCs = [];
    level.zAI_Alarm = AI_CreateAlarm();
    
    level thread AI_Spawner();
}

AI_Patrol()
{
    self endon("death");
    self endon("zdeath");
    
    test_waypoints = [];
    test_waypoints[test_waypoints.size] = (-1376, 5321, 2776);
    test_waypoints[test_waypoints.size] = (-1357, 6569, 2648);
    test_waypoints[test_waypoints.size] = (-955, 6561, 2648);
    test_waypoints[test_waypoints.size] = (12, 6870, 2648);
    test_waypoints[test_waypoints.size] = (226, 6855, 2648);
    test_waypoints[test_waypoints.size] = (391, 6678, 2648);
    test_waypoints[test_waypoints.size] = (655, 6723, 2648);
    test_waypoints[test_waypoints.size] = (650, 6930, 2648);
    test_waypoints[test_waypoints.size] = (1068, 6951, 2824);
    test_waypoints[test_waypoints.size] = (1062, 6830, 2824);
    test_waypoints[test_waypoints.size] = (645, 6652, 2824);
    test_waypoints[test_waypoints.size] = (-242, 6616, 2776);
    test_waypoints[test_waypoints.size] = (-303, 6000, 2776);
    test_waypoints[test_waypoints.size] = (-1194, 5349, 2776);
    
    idx = 0;
    
    for(;;) {
        //wait(0.01);
        
        offset = (0, 0, 0);
        
        while(self.origin[0] != (test_waypoints[idx][0] + offset[0])
        && self.origin[1] != (test_waypoints[idx][1] + offset[1])) { // шагаем к следующей точке
            angles_to_wp = vectorToAngles((test_waypoints[idx] + offset) - self.origin);
            dist_to_wp = distance2d(test_waypoints[idx] + offset, self.origin);
            trace_z = bulletTrace(self.origin + (0, 0, 80), self.origin + (0, 0, -40), false, self);
            time = dist_to_wp/100;
            
            if(isDefined(trace_z["entity"]) && trace_z["entity"] == self.box)
                trace_z = bulletTrace(trace_z["entity"].origin, self.origin + (0, 0, -40), false, trace_z["entity"]);
            
            // TODO: переделать ходьбу "рывками"
            self.origin = (self.origin[0], self.origin[1], trace_z["position"][2]);
            
            self scriptModelPlayAnim("pb_walk_forward_mg");
            self.head scriptModelPlayAnim("pb_walk_forward_mg");
            
            self rotateTo((0, angles_to_wp[1], 0), 0.15);
            self moveTo(test_waypoints[idx] + offset, time);
            
            wait(0.2);
        }
        
        idx = (idx + 1) % test_waypoints.size;
    }
}

AI_Shellshock()
{
    self notify("zshellshock");
    self endon("zshellshock");
    self endon("death");
    self endon("zdeath");
    
    self.isBeingFlashed = true;
    self moveTo(self.origin, 0.1); // stop moving
    
    if(self.isCrouching)
        self scriptModelPlayAnim(self.anims["shellshock_crouch"]);
    else
        self scriptModelPlayAnim(self.anims["shellshock_stand"]);
    
    wait(3.5);
    
    self.isBeingFlashed = false;
}

AI_Spawner()
{
    level endon("game_ended");
    
    level waittill("zmasterbot_spawned");
    
    for(;;) {
        wait(60);
        
        if(cointoss()) { // every 60 sec is to spawn the sniper with 50/50 chance
            level.zAI_NPCs[AI_GetFreeSlot()] = AI_SpawnSniperUnit();
        }
    }
    
    //for(;;) {
    //    if(level.script == "mp_highrise") {
    //        thread AI_SpawnSquad("normal", 8, 0.5, 0);
    //    }
    //    
    //    wait(1337);
    //}
}

AI_SpawnSniperUnit()
{
    spawn_num = randomInt(level.zMap_DefendersSniperSpawns.size);
    spawn_origin = level.zMap_DefendersSniperSpawns[spawn_num].origin;
    spawn_angles = level.zMap_DefendersSniperSpawns[spawn_num].angles;
    
    announcement("Trying to spawn the sniper!");
    
    if(isDefined(level.zMap_DefendersSniperSpawns[spawn_num].busy))
        return undefined;
    
    unit = spawn("script_model", spawn_origin);
    unit setModel(level.zMap_DefendersUnits["_sniper"].body);
    
    unit.head = spawn("script_model", unit getTagOrigin("j_spine4"));
    unit.head.angles = (270, 0, 270);
    unit.head setModel(level.zMap_DefendersUnits["_sniper"].head);
    unit.head linkTo(unit, "j_spine4");
    
    unit.weapon = spawn("script_model", unit getTagOrigin("tag_weapon_right"));
    unit.weapon.name = level.zMap_DefendersUnits["_sniper"].weapon;
    unit.weapon.camo = level.zMap_DefendersUnits["_sniper"].camo;
    unit.weapon.delay = 2.0;
    unit.weapon setModel(getWeaponModel(unit.weapon.name, unit.weapon.camo));
    unit.weapon z_WeaponModelManageTags(unit.weapon.name);
    unit.weapon linkTo(unit, "tag_weapon_right");
    
    unit.vec = spawn("script_origin", unit getTagOrigin("tag_weapon_right"));
    unit.vec linkTo(unit.weapon, "tag_flash", (50, 0, 0), (0, 0, 0));
    
    unit.box = spawn("script_model", unit getTagOrigin("j_spinelower") + (-5, 0, -5));
    unit.box setModel("com_plasticcase_beige_big");
    unit.box cloneBrushmodelToScriptmodel(level.airDropCrateCollision);
    unit.box.angles = (90, 0, 0);
    unit.box hide();
    unit.box setCanDamage(true);
    unit.box.health = 9999;
    unit.box.maxhealth = 9999;
    unit.box.hp = 100;
    unit.box linkTo(unit, "j_spinelower");
    
    unit.anims = [];
    unit.anims["ads_crouch"] = "pb_crouch_ads";
    unit.anims["death_crouch"] = "pb_crouch_death_clutchchest";
    unit.anims["idle_crouch"] = "pb_crouch_alert";
    unit.anims["shellshock_crouch"] = "pb_crouch_shellshock";
    
    unit.isBeingFlashed = false;
    unit.isCrouching = true;
    unit.isShooting = false;
    
    unit.extraVision = 50;
    
    unit.type = "_sniper";
    
    unit scriptModelPlayAnim(unit.anims["ads_crouch"]);
    unit.head scriptModelPlayAnim(unit.anims["ads_crouch"]);
    
    unit rotateTo(spawn_angles, 0.05);
    
    unit thread AI_AttackPlayers();
    unit thread AI_Damage();
    
    level.zMap_DefendersSniperSpawns[spawn_num].busy = unit;
    
    return unit;
}

AI_SpawnSquad()
{
    //
}

AI_SpawnUnit(pos)
{
    unit = spawn("script_model", pos);
    unit setModel("mp_body_us_army_smg_b");
    
    unit.head = spawn("script_model", unit getTagOrigin("j_spine4"));
    unit.head.angles = (270, 0, 270);
    unit.head setModel("head_us_army_a");
    unit.head linkTo(unit, "j_spine4");
    
    unit.weapon = spawn("script_model", unit getTagOrigin("tag_weapon_right"));
    unit.weapon.name = "mp5k_reflex_silencer_mp";
    unit.weapon.camo = 0;
    unit.weapon setModel(getWeaponModel(unit.weapon.name, unit.weapon.camo));
    unit.weapon z_WeaponModelManageTags(unit.weapon.name);
    unit.weapon linkTo(unit, "tag_weapon_right");
    
    unit.vec = spawn("script_origin", unit getTagOrigin("tag_weapon_right"));
    unit.vec linkTo(unit.weapon, "tag_flash", (50, 0, 0), (0, 0, 0));
    
    unit.box = spawn("script_model", unit getTagOrigin("j_spinelower") + (-5, 0, -5));
    unit.box setModel("com_plasticcase_beige_big");
    unit.box cloneBrushmodelToScriptmodel(level.airDropCrateCollision);
    unit.box.angles = (90, 0, 0);
    unit.box hide();
    //unit.box setCanDamage(true);
    //unit.box.health = 9999;
    //unit.box.maxhealth = 9999;
    //unit.box.hp = 100;
    unit.box linkTo(unit, "j_spinelower");
    
    unit scriptModelPlayAnim("pb_stand_alert");
    unit.head scriptModelPlayAnim("pb_stand_alert");
    
    unit thread AI_Patrol();
    
    return unit;
}

masterBot_Logic()
{
    self endon("disconnect");
    
    while(!isDefined(self.pers["team"]))
        wait(0.1);
    
    self notify("menuresponse", game["menu_team"], level.zCfg_Defenders);
    
    wait(0.5);
    
    for(;;) {
        self notify("menuresponse", "changeclass", "class4");
        
        self waittill("spawned_player");
        self setOrigin(level.zMap_MasterBotSpawnOrigin);
        self setPlayerAngles(level.zMap_MasterBotSpawnAngles);
        
        level notify("zmasterbot_spawned", self);
        
        wait(0.1);
    }
}

masterBot_Spawner()
{
    z_WaittillGameCreated();
    wait(2.5); // do not spawn the bot too early
    
    for(;;) {
        level.zMasterBot = addTestClient();
        
        if(!isDefined(level.zMasterBot)) {
            announcement("Error during adding the master bot");
            
            wait(0.25);
            continue;
        }
        
        level.zMasterBot.pers["master_bot"] = true;
        level.zMasterBot thread masterBot_Logic();
        
        level.zMasterBot waittill("disconnect");
        wait(0.25);
    }
}

precacheAnimations()
{
    //precacheMpAnim("pb_stand_death_shoulder_stumble"); // смерть 1
    //precacheMpAnim("pb_stumble_walk_forward"); // walking
    //precacheMpAnim("pb_walk_forward_mg"); // ходьба джаггернаута
    //precacheMpAnim("pb_run_fast"); // очень быстрый забавный бег
    //precacheMpAnim("pb_sprint"); // бежит так быстро, что аж бомбит
    //precacheMpAnim("pb_sprint_gundown"); // бежит быстро, норм так
    //precacheMpAnim("pb_sprint_hold"); // бежит быстро с С4
    //precacheMpAnim("pt_reload_stand_rifle"); // перезарядка быстрая
    //precacheMpAnim("pt_reload_stand_mg"); // перезарядка долгая (пулемет)
    //precacheMpAnim("pb_stand_shoot_walk_forward_unarmed"); // анимация ходьбы в развалочку
    //precacheMpAnim("pb_stand_shoot_walk_forward");
    //precacheMpAnim("pb_stand_shellshock"); // контузия
    
    // бездействие
    precacheMpAnim("pb_stand_alert"); // стоя
    precacheMpAnim("pb_crouch_alert"); // сидя
    
    // ходьба
    precacheMpAnim("pb_walk_forward_mg"); // стоя
    
    // бездействие, в руках С4
    //precacheMpAnim("pb_hold_idle"); // стоя
    //precacheMpAnim("pb_crouch_hold_idle"); // сидя
    
    // прицеливание/стрельба на месте
    precacheMpAnim("pb_stand_ads"); // стоя
    precacheMpAnim("pb_crouch_ads"); // сидя
    
    // смерть
    precacheMpAnim("pb_stand_death_shoulder_stumble"); // стоя
    precacheMpAnim("pb_crouch_death_clutchchest"); // сидя
    
    // контузия
    precacheMpAnim("pb_stand_shellshock"); // стоя
    precacheMpAnim("pb_crouch_shellshock"); // сидя
    
    // использование ноутбука
    //precacheMpAnim("pb_stand_remotecontroller"); // стоя
    //precacheMpAnim("pb_crouch_remotecontroller"); // сидя
}
*/

